package cn.wgx.modules.sys.web;

import cn.wgx.commons.security.shiro.filter.FormAuthenticationFilter;
import cn.wgx.commons.security.shiro.realm.CustomRealm;
import cn.wgx.commons.util.CacheUtils;
import cn.wgx.commons.util.IdGen;
import cn.wgx.commons.util.StringUtils;
import cn.wgx.commons.util.ValidateCodeImageUtil;
import cn.wgx.modules.base.web.BaseController;
import cn.wgx.modules.sys.entity.SysUser;
import cn.wgx.modules.sys.util.UserUtil;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;

@Api(tags = "1. 登录/登出管理入口")
@RestController
@RequestMapping(value = "${allPathPre}login")
public class LoginController extends BaseController {

    public static final String VALIDATE_CODE = "validateCode";

    private static int isValidateCodeNum = 3;


    @ApiIgnore()
    @RequestMapping(value = "", method = RequestMethod.GET)
    public Map<String, Object> loginget() {
        return returnData("index.html");
    }

    /**
     * 此处为登录失败处理,真正登录入口在shiroFilter
     *
     * @param username
     * @param password
     * @param validateCode
     * @param rememberMe
     * @param request
     * @return
     */
    @ApiOperation(value = "登录入口,只需用户名/密码,remeberme记住我,保持30天免登陆. 连续登录错误3次以上需要提交验证码.")
    @RequestMapping(value = "", method = RequestMethod.POST)
    public Map<String, Object> login(@RequestParam String username,
                                     @RequestParam String password,
                                     @ApiParam("登录类型:1:用户名登录,2:邮箱登录,3:手机号登录.默认为1") @RequestParam(defaultValue = "1") Integer loginType,  //todo 登录身份类型
                                     @RequestParam(required = false) String validateCode,
                                     @RequestParam(defaultValue = "true") boolean rememberMe,
                                     HttpServletRequest request) {
        Map<String, Object> map = getInitMap();
        CustomRealm.Principal principal = UserUtil.getPrincipal();

        // 如果已经登录，则跳转到管理首页
        if (principal != null) {
            SysUser sysUser = UserUtil.getUser();
            return returnData(sysUser);
        }

        String exception = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
        String message = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM);

        if (StringUtils.isBlank(message) || StringUtils.equals(message, "null")) {
            message = "用户名或密码错误, 请重试..";
        }

//        map.put(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, username);
//        map.put(FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM, rememberMe);
//        map.put(FormAuthenticationFilter.DEFAULT_MOBILE_PARAM, mobile);
//        map.put(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME, exception);

        // 非授权异常，登录失败，验证码加1。
        if (!UnauthorizedException.class.getName().equals(exception)) {
            map.put("isValidateCodeLogin", isValidateCodeLogin(username, true, false));
        }
        // 验证失败清空验证码
        request.getSession().setAttribute(VALIDATE_CODE, IdGen.uuid());

        returnErrorMap(message, map);

        return map;
    }

    /**
     * 登录成功自动后进入此入口,不对外暴露
     *
     * @param request
     * @param response
     * @return
     */
    @ApiIgnore()
    @RequestMapping(value = "success", method = RequestMethod.GET)
    public Map<String, Object> index(HttpServletRequest request, HttpServletResponse response) {
        CustomRealm.Principal principal = UserUtil.getPrincipal();
        if (principal != null) {
        // 登录成功后，验证码计算器清零
        isValidateCodeLogin(principal.getUsername(), false, true);
        // 如果已登录，再次访问主页，则退出原账号。
//        if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){
//            String logined = CookieUtils.getCookie(request, "LOGINED");
//            if (StringUtils.isBlank(logined) || "false".equals(logined)){
//                CookieUtils.setCookie(response, "LOGINED", "true");
//            }else if (StringUtils.equals(logined, "true")){
//                UserUtil.getSubject().logout();
//                return map;
//            }
//        }
        SysUser sysUser = principal.getUser();
        Map<String, Object> map = returnData(sysUser);
        map.put("menus", UserUtil.getUserMenus(true, true));
        return map;
        }
        return returnErrorMap("失败");
    }


    @ApiOperation("获取登录验证码,登录错误3次以上需要")
    @RequestMapping(value = "validateCode", method = RequestMethod.GET)
    public void validateCode(HttpServletResponse response, HttpSession session) {
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        ValidateCodeImageUtil v = new ValidateCodeImageUtil();
        String vCode = v.createVCode();
        BufferedImage image = v.createImage(vCode);
        OutputStream out = null;
        session.setAttribute(VALIDATE_CODE, vCode);
        try {
            out = response.getOutputStream();
            ImageIO.write(image, "JPEG", out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @ApiOperation("测试用: 检测用户权限权限. 可检测特殊权限,type可选('role','permission')")
    @RequestMapping(value = "checkRoleAndPermission", method = RequestMethod.GET)
    public Map<String, Object> checkRoleAndPer(@RequestParam(required = false, defaultValue = "") String type,
                                               @RequestParam(required = false, defaultValue = "") String value) {
        Map<String, Object> map = getInitMap();
        Subject subject = SecurityUtils.getSubject();
        map.put("isAuthenticated", subject.isAuthenticated());
        map.put("hasRole:admin", subject.hasRole("admin"));
        map.put("hasRole:User", subject.hasRole("user"));
        map.put("isRemembered", subject.isRemembered());
        switch (type) {
            case "role":
                map.put("hasRole:" + value, subject.hasRole(value));
            case "permission":
                map.put("isPermitted:" + value, subject.isPermitted(value));
        }
        return map;
    }


    @ApiOperation("退出登录")
    @RequestMapping(value = "logout", method = RequestMethod.GET)
    public Map<String, Object> logout(HttpServletRequest request, HttpServletResponse response) {
        UserUtil.logOut();
        return returnData("退出登录成功.");
    }


    /**
     * 是否是验证码登录
     *
     * @param useruame 用户名
     * @param isFail   计数加1
     * @param clean    计数清零
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean isValidateCodeLogin(String useruame, boolean isFail, boolean clean) {
        Map<String, Integer> loginFailMap = (Map<String, Integer>) CacheUtils.get("loginFailMap");
        if (loginFailMap == null) {
            loginFailMap = Maps.newHashMap();
            CacheUtils.put("loginFailMap", loginFailMap);
        }
        Integer loginFailNum = loginFailMap.get(useruame);
        if (loginFailNum == null) {
            loginFailNum = 0;
        }
        if (isFail) {
            loginFailNum++;
            loginFailMap.put(useruame, loginFailNum);
        }
        if (clean) {
            loginFailMap.remove(useruame);
        }
        return loginFailNum >= isValidateCodeNum;
    }

    @Value("${isValidateCodeNum}")
    public void setIsValidateCodeNum(int isValidateCodeNum) {
        LoginController.isValidateCodeNum = isValidateCodeNum;
    }
}
